﻿/* ==============================================================================
*
*   Copyright (c) 2010, Pierre Favier
*   All rights reserved.
*
*   NTasking is free software: you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation.
*
*   Redistribution and use in source and binary forms, with or without
*   modification, are permitted provided that the following conditions
*   are met:
*
*   -   Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*
*   -   Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials
*       provided with the distribution.
*
*   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
*   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
*   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
*   THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*   SUCH DAMAGE.
*
* ============================================================================== */

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace NTasking.Internals
{

    /// <summary>
    /// Encapsulate a managed <see cref="Thread"/> to augment its functionalities
    /// </summary>
    internal partial class ThreadWrapper : ITask, ITaskInternal
    {

        #region Static Fields

        /// <summary>
        /// The instance associated to the current thread
        /// </summary>
        [ThreadStatic]
        private static ThreadWrapper _currentThread;

        #endregion


        #region Static Properties

        /// <summary>
        /// Gets or sets the instance associated with the current thread.
        /// </summary>
        /// <value>The wrapper around the current thread.</value>
        internal static ThreadWrapper CurrentThread
        {
            get
            {
                if (_currentThread == null)
                    throw Errors.NoMasterBlock();

                return _currentThread;
            }
            set
            {
                _currentThread = value;
            }
        }

        #endregion


        #region Fields

        /// <summary>
        /// Stores a list of synchronized calls performed by the current thread.
        /// </summary>
        private readonly Stack<SynchronizedMethod> _callsInProgress = new Stack<SynchronizedMethod>();

        /// <summary>
        /// Store the list of imbricated tasking scopes
        /// </summary>
        private readonly List<Block> _blockStack = new List<Block>();


        /// <summary>
        /// Stores the list of selective call which are waiting for completion in the current instance
        /// </summary>
        private readonly Stack<MethodCall> _callsWaitingForCompletion = new Stack<MethodCall>();


        /// <summary>
        /// A value indicating the prefix to be applied when formatting this instance.
        /// </summary>
        private readonly string _namePrefix;


        /// <summary>
        /// An object used to lock againts changes of the WaitState property
        /// </summary>
        internal readonly object WaitStateLock = new object();

        #endregion


        #region Properties

        /// <summary>
        /// Gets a value indicating the current status of this instance.
        /// </summary>
        /// <value>The current status of this instance.</value>
        public TaskStatus TaskStatus
        {
            get
            {
                ThreadState state = Thread.ThreadState;

                if ((state & (ThreadState.Aborted | ThreadState.AbortRequested)) != 0)
                    return TaskStatus.Abnormal;

                if ((state & ThreadState.Stopped) != 0)
                    return TaskStatus.Terminated;

                if ((state & (ThreadState.WaitSleepJoin | ThreadState.Suspended)) != 0)
                    return TaskStatus.Blocked;

                return TaskStatus.Ready;
            }
        }


        /// <summary>
        /// Gets a value indicating whether the current instance is Callable.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if the current instance is Callable, <c>false</c> otherwise.
        /// </value>
        public bool Callable
        {
            get { return !Terminated; }
        }


        /// <summary>
        /// Gets a value indicating whether the current instance is Terminated.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if the current instance is Terminated, <c>false</c> otherwise.
        /// </value>
        public bool Terminated
        {
            get { return (Thread.ThreadState & (ThreadState.Stopped | ThreadState.Aborted)) != 0; }
        }


        /// <summary>
        /// Gets a value indicating whether this instance can block.
        /// </summary>
        /// <value><c>true</c> if this instance can block; otherwise, <c>false</c>.</value>
        internal bool CanBlock
        {
            get
            {
                return _callsInProgress.Count == 0 || _callsInProgress.Peek().Owner.IsThreaded;
            }
        }


        /// <summary>
        /// Gets or sets the name of the encapsulad thread.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return Thread.Name; }
            set
            {
                if (string.IsNullOrEmpty(Thread.Name))
                    Thread.Name = value;
                else
                    throw Errors.TaskNameNotChangeable(this);
            }
        }


        /// <summary>
        /// Gets a value indicating whether the call in progress is a read-only call.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if the call in progress is a read-only call; otherwise, <c>false</c>.
        /// </value>
        internal bool ReadOnlyOperationInProgress
        {
            get
            {
                return _callsInProgress.Count > 0 && _callsInProgress.Peek().IsReadOnly;
            }
        }


        /// <summary>
        /// Gets the call currently beeing executed.
        /// </summary>
        /// <value>The call beeing executed.</value>
        internal SynchronizedMethod CallInProgress { get { return _callsInProgress.Count == 0 ? null : _callsInProgress.Peek(); } }


        /// <summary>
        /// Gets or sets the thread encapsulated by this instance.
        /// </summary>
        /// <value>The thread.</value>
        private Thread Thread { get; set; }


        /// <summary>
        /// Gets the state of the thread.
        /// </summary>
        /// <value>The state of the thread.</value>
        internal ThreadState ThreadState
        {
            get { return Thread.ThreadState; }
        }


        /// <summary>
        /// Gets or sets the lock on which acquisition the current thread is blocked.
        /// </summary>
        /// <value>The blocked on lock.</value>
        internal object BlockedOnLock { get; set; }


        /// <summary>
        /// Gets a value indicating whether the current thread is in the execution of an ebaortable part.
        /// </summary>
        /// <value><c>true</c> if [in abortable part]; otherwise, <c>false</c>.</value>
        internal bool InAbortablePart
        {
            get
            {
                return _callsWaitingForCompletion.Count > 0 &&
                       _callsWaitingForCompletion.Peek().WaitState == CallerWaitState.ExecuteAbortablePart;
            }
        }


        /// <summary>
        /// Gets or sets the <see cref="ManagedLock"/> used to prevent against concurrent access
        /// to this instance.
        /// </summary>
        /// <value>The <see cref="ManagedLock"/> used to prevent against concurrent access.</value>
        internal ManagedLock CallLock { get; private set; }

        #endregion


        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ThreadWrapper"/> class.
        /// </summary>
        internal ThreadWrapper()
            : this(typeof(ThreadWrapper).Name, Thread.CurrentThread, true)
        {
            CurrentThread = this;
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="ThreadWrapper"/> class.
        /// </summary>
        /// <param name="namePrefix">A value indicating the prefix to be applied when formatting this instance.</param>
        /// <param name="thread">The thread encapsulated by the new instance.</param>
        /// <param name="chekMaster">A flag indicating whether the existence of a Master block must be checked</param>
        internal ThreadWrapper(string namePrefix, Thread thread, bool chekMaster = false)
        {
            if (chekMaster && Block.Environment == null)
                throw Errors.NoMasterBlock();

            _basePriority = Thread.CurrentThread.Priority;
            Thread = thread;
            _namePrefix = namePrefix;
            CallLock = new ManagedLock(this + ".MethodCall", this);

            _noAbort = new CodeWrapper(AbortDefer, AbortResume);
            _withAbort = new CodeWrapper(AbortResume, AbortDefer);
        }

        #endregion


        #region Methods

        /// <summary>
        /// Starts this instance.
        /// </summary>
        internal void Start()
        {
            Thread.Start();
        }


        /// <summary>
        /// Blocks the calling thread until this instance terminates.
        /// </summary>
        internal void Join()
        {
// ReSharper disable PossibleNullReferenceException
            if (Thread.CurrentThread != Thread && Thread.ThreadState != ThreadState.Unstarted)
// ReSharper restore PossibleNullReferenceException
                Thread.Join();
        }


        /// <summary>
        /// Get the innermost <see cref="Block"/> currently being executed.
        /// </summary>
        /// <returns>The innermost <see cref="Block"/> or <c>null</c> if no <see cref="Block"/> has been started</returns>
        internal Block CurrentBlock()
        {
            return _blockStack.Count == 0 ? null : _blockStack[_blockStack.Count - 1];
        }


        /// <summary>
        /// Signal that this instance is entering a new <see cref="Block"/>.
        /// </summary>
        /// <param name="scope">The new <see cref="Block"/>.</param>
        internal void EnterBlock(Block scope)
        {
            _blockStack.Add(scope);
        }


        /// <summary>
        /// Signal that this instance is leaving a <see cref="Block"/>.
        /// </summary>
        internal void LeaveBlock(Block scope)
        {
            _blockStack.Remove(scope);
        }


        /// <summary>
        /// Signal that the thread starts waiting for completion of the specified call.
        /// </summary>
        /// <param name="call">The call for which completion to wait.</param>
        internal void StartWaitingForCompletion(MethodCall call)
        {
            lock (_priorityLock)
                _callsWaitingForCompletion.Push(call);
        }


        /// <summary>
        /// Signal that the specified last has completed.
        /// </summary>
        internal void EndWaitingForCompletion()
        {
            lock (_priorityLock)
                _callsWaitingForCompletion.Pop();
        }


        string ITaskInternal.StateString()
        {
            return StateString(0);
        }


        /// <summary>
        /// Builds a string that represents the current state of the thread
        /// with regards to running/waiting/accepting/....
        /// </summary>
        /// <param name="pad">The amount of whitespace to append at the head of the string.</param>
        /// <returns></returns>
        internal string StateString(int pad)
        {
            StringBuilder builder = new StringBuilder();
            int padding = pad;

            foreach (MethodCall call in _callsWaitingForCompletion)
            {
                if (builder.Length > 0)
                    builder.AppendLine();

                builder.Append(new string(' ', padding++));
                builder.Append(call.StateString());

                if (call.WaitState == CallerWaitState.NoWait) break;
            }

            object blockedOn = BlockedOnLock;

            if (blockedOn != null)
            {
                if (builder.Length > 0)
                    builder.AppendLine();
                builder.Append(new string(' ', padding));
                builder.Append("Waiting for lock " + blockedOn);
            }

            if (builder.Length == 0)
                builder.Append("Running");

            return builder.ToString();
        }
        
        
        /// <summary>
        /// Signal that this instance is starting execution of a new synchronized call.
        /// </summary>
        /// <param name="call">The synchronized call.</param>
        internal void PushCall(SynchronizedMethod call)
        {
            _callsInProgress.Push(call);
        }


        /// <summary>
        /// Signal that this instance is finishing execution of a synchronized call.
        /// </summary>
        internal void PopCall()
        {
            _callsInProgress.Pop();
        }


        /// <summary>
        /// Check if the current instance is executing a synchronized call of the specified <see cref="SynchronizedObject"/>.
        /// </summary>
        /// <param name="owner">The <see cref="SynchronizedObject"/> to be checked.</param>
        /// <returns><c>true</c> if the current instance is executing a synchronized call of the specified <see cref="SynchronizedObject"/>.</returns>
        internal bool InternalCall(SynchronizedObject owner)
        {
            return (_callsInProgress.Count != 0 && _callsInProgress.Peek().Owner == owner);
        }


        /// <summary>
        /// Check if the current instance is recursively executing a synchronized call of the specified <see cref="SynchronizedObject"/>.
        /// </summary>
        /// <param name="owner">The <see cref="SynchronizedObject"/> to be checked.</param>
        /// <returns><c>true</c> if the current instance is recursively executing a synchronized call of the specified <see cref="SynchronizedObject"/></returns>
        internal bool RecursiveCall(SynchronizedObject owner)
        {
            SynchronizedMethod[] operationsInProgress = _callsInProgress.ToArray();

            for (int i = operationsInProgress.Length; i > 0; i--)
            {
                if (operationsInProgress[i - 1].Owner == owner)
                    return true;
            }

            return false;
        }


        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            string name = Name;

            return _namePrefix + (string.IsNullOrEmpty(name) ? "" : "[" + name + "]");
        }

        #endregion

    }

}
